use crate::json_token::JsonToken;
use std::collections::LinkedList;

/// 词法分析器

/// 这个宏可以用来让 JsonLexer 能够打印
#[derive(Debug)]
pub struct JsonLexer<'a> {
    input: &'a str,
    pos: usize,
}

impl<'a> JsonLexer<'a> {
    pub fn new(input: &'a str) -> Self {
        Self {
            input: input,
            pos: 0,
        }
    }

    /// 创建一个切片, 获取一个迭代器
    /// "我喜欢吃饭"      self.pos = 3
    /// self.input[self.pos..].chars().next() // 喜欢吃饭   这里汉字utf8编码占3位
    /// self.input.chars().nth(self.pos)      // 吃饭        这里相当于下标
    fn peek_char(&mut self) -> Option<char> {
        self.input[self.pos..].chars().next()
    }

    /// 计算位置, 看是遍历到了哪个位置
    fn move_to_next(&mut self, c: char) {
        self.pos += c.len_utf8();
    }

    /// 在调用它的函数那里进行递归
    /// 调用这个函数, 这个函数每次都只会执行一次, 只用拿到一个 token 进行个接下来的操作
    pub fn next_token(&mut self) -> Option<JsonToken> {
        loop {
            match self.peek_char() {
                Some(c) => match self.handle_char(c) {
                    Some(token) => break Some(token),
                    None => continue,
                },
                None => break None,
            }
        }
    }

    /// 模式匹配, 遇到什么就去对应的 处理器 处理
    fn handle_char(&mut self, mut c: char) -> Option<JsonToken> {
        while c.is_whitespace() {
            self.move_to_next(c);
            c = self.peek_char()?;
        }
        match c {
            '"' => self.handle_string(),
            '0'..='9' | 'e' | 'E' | '.' | '+' | '-' => self.handle_number(),
            't' | 'f' => self.handle_boolean(),
            'n' => self.handle_null(),
            ',' => {
                self.move_to_next(',');
                Some(JsonToken::Comma)
            }
            ':' => {
                self.move_to_next(':');
                Some(JsonToken::Colon)
            }
            '{' => {
                self.move_to_next('{');
                Some(JsonToken::LeftBrace)
            }
            '}' => {
                self.move_to_next('}');
                Some(JsonToken::RightBrace)
            }
            '[' => {
                self.move_to_next('[');
                Some(JsonToken::LeftBracket)
            }
            ']' => {
                self.move_to_next(']');
                Some(JsonToken::RightBracket)
            }
            _ => {
                panic!("Unexpected character: {}", c);
            }
        }
    }

    /// 匹配不同的符号, 然后把他们返回成为 16进制 的形式
    /// 当匹配的字符是 u 就说明匹配到了真正的数据, 先进行 16进制 转码, 等遍历完后把它变成 char 类型的
    fn handle_escape(&mut self) -> Option<char> {
        self.move_to_next('\\');
        match self.peek_char() {
            Some('"') => {
                self.move_to_next('"');
                Some('"')
            }
            Some('\\') => {
                self.move_to_next('\\');
                Some('\\')
            }
            Some('/') => {
                self.move_to_next('/');
                Some('/')
            }
            Some('b') => {
                //当前匹配到的字符是 'b'，表示这是一个退格符转义序列（\b）
                self.move_to_next('b');
                Some('\x08') //\x08 是十六进制表示，对应的 ASCII 码为 8，表示退格符。
            }
            Some('f') => {
                //当前匹配到的字符是 'f'，表示这是一个换页符转义序列（\f）。
                self.move_to_next('f');
                Some('\x0c') //\x0c 是十六进制表示，对应的 ASCII 码为 12，表示换页符。
            }
            Some('n') => {
                self.move_to_next('n');
                Some('\n')
            }
            Some('r') => {
                self.move_to_next('r');
                Some('\r')
            }
            Some('t') => {
                self.move_to_next('t');
                Some('\t')
            }
            Some('u') => {
                // 当前匹配到的字符是 'u'，这通常表示接下来是一个 Unicode 转义序列（如 \u0041）。
                self.move_to_next('u');
                let mut codepoint = 0;
                for _ in 0..4 {
                    match self.peek_char() {
                        Some(c) if c.is_digit(16) => {
                            // 解析过程如下：
                            // 读取第一个字符 '0'，codepoint = 0 * 16 + 0 = 0。
                            // 读取第二个字符 '0'，codepoint = 0 * 16 + 0 = 0。
                            // 读取第三个字符 '4'，codepoint = 0 * 16 + 4 = 4。
                            // 读取第四个字符 '1'，codepoint = 4 * 16 + 1 = 65。
                            // 最终，codepoint 的值为 65，即 Unicode 字符 'A' 的代码点。
                            codepoint = codepoint * 16 + c.to_digit(16)?;
                            self.move_to_next(c);
                        }
                        _ => return None,
                    }
                }
                // 把 16进制 转为 char 类型
                std::char::from_u32(codepoint).or(Some('\u{fffd}'))
            }
            _ => None,
        }
    }

    /// 匹配到是字符串 就使用模式匹配看是不是 特殊字符 或者
    fn handle_string(&mut self) -> Option<JsonToken> {
        self.move_to_next('"');
        let mut string = String::new();
        loop {
            match self.peek_char() {
                Some('"') => {
                    self.move_to_next('"');
                    break Some(JsonToken::String(string));
                }
                Some('\\') => match self.handle_escape() {
                    Some(c) => string.push(c),
                    None => {
                        string.push('\\');
                        if let Some(next) = self.peek_char() {
                            string.push(next);
                            self.move_to_next(next);
                        } else {
                            break None;
                        }
                    }
                },
                Some(c) => {
                    string.push(c);
                    self.move_to_next(c);
                }
                None => return None,
            }
        }
    }

    /// 如果匹配到数字就把他收集成字符串再变成数字
    fn handle_number(&mut self) -> Option<JsonToken> {
        let mut number = String::new();
        let c = self.peek_char()?;
        number.push(c);
        self.move_to_next(c);
        while let Some(c) = self.peek_char() {
            if c.is_digit(10) || c == 'e' || c == 'E' || c == '.' || c == '+' || c == '-' {
                number.push(c);
                self.move_to_next(c);
            } else {
                break;
            }
        }
        match number.parse::<f64>() {
            Ok(n) => Some(JsonToken::Number(n)),
            Err(_) => None,
        }
    }

    fn handle_boolean(&mut self) -> Option<JsonToken> {
        let mut boolean = String::new();
        let c = self.peek_char()?;
        boolean.push(c);
        self.move_to_next(c);
        while let Some(c) = self.peek_char() {
            if c.is_ascii_alphabetic() {
                boolean.push(c);
                self.move_to_next(c);
            } else {
                break;
            }
        }
        match boolean.as_str() {
            "true" => Some(JsonToken::Boolean(true)),
            "false" => Some(JsonToken::Boolean(false)),
            _ => None,
        }
    }

    fn handle_null(&mut self) -> Option<JsonToken> {
        let mut null = String::new();
        null.push('n');
        self.move_to_next('n');
        while let Some(c) = self.peek_char() {
            if c.is_ascii_alphabetic() {
                null.push(c);
                self.move_to_next(c);
            } else {
                break;
            }
        }
        match null.as_str() {
            "null" => Some(JsonToken::Null),
            _ => None,
        }
    }
}

pub fn parse_all(input: &str) -> Option<LinkedList<JsonToken>> {
    let mut lexer = JsonLexer::new(input);
    // println!("lexer {:?}", lexer);
    let mut tokens = LinkedList::new();
    loop {
        match lexer.next_token() {
            Some(token) => {
                // println!("token {:?}", token);
                tokens.push_back(token)
            }
            None => break Some(tokens.clone()),
        }
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_16_char() {
        let codepoint = 0x41;
        let result = std::char::from_u32(codepoint).or(Some('\u{fffd}'));
        println!("{:?}", result); // 输出：Some('A')
    }

    #[test]
    fn test_parse_all() {
        let json_str = "{\"first name\":\"Yiran\", \"last name\": \"王\", \"age\":20,\"is_student\":true,\"courses\":[{\"name\":\"CPEN 212\",\"credits\":4},{\"name\":\"CPSC 221\",\"credits\":4},{\"name\":\"Math 256\",\"credits\":3}],\"address\":{\"street\":\"1935 Lower Mall\",\"city\":\"Vancouver\",\"postal_code\":\"V6T 1X1\"},\"friends\":[{\"name\":\"Tommy\",\"age\":20},{\"name\":\"Joe\",\"age\":20}],\"graduated\":false, \"university\": \"UBC\"}";
        let res = parse_all(json_str).unwrap();
        println!("{:?}", res);
    }
}
